//   MVCWin32 (Window.cpp)
//
//   Copyright 2010 Eric Llewellyn
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

#include "Window.h"
#include "Application.h"

#include <windows.h>

namespace MVCWin32
{
	//Static
	std::map<HWND, Window*> Window::windowMap;

	//Static
	void Window::RegisterClass()
	{
		WNDCLASSEX windowClass;
		windowClass.cbSize = sizeof(WNDCLASSEX);
		windowClass.style = 0;
		windowClass.lpfnWndProc = Window::MessageRouter;
		windowClass.cbClsExtra = 0;
		windowClass.cbWndExtra = 0;
		windowClass.hInstance = NULL;
		windowClass.hIcon = NULL;
		windowClass.hCursor = NULL;
		windowClass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
		windowClass.lpszMenuName = NULL;
		windowClass.lpszClassName = L"WindowClass";
		windowClass.hIconSm = NULL;
		RegisterClassEx(&windowClass);
	}

	//Static
	LRESULT CALLBACK Window::MessageRouter(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		Window* win = (Window*)Window::windowMap[hWnd];

		if(win == 0)
			return DefWindowProc(hWnd,message,wParam,lParam);
		else
			return win->WindowProc(hWnd,message,wParam,lParam);
	}

	Window::Window()
	{
		this->delegatePtr=0;
		this->view=0;

		if(windowMap.size() <= 0)
			Window::RegisterClass();

		LPCTSTR className = L"WindowClass";
		LPCTSTR name = L"";
		DWORD style = WS_OVERLAPPEDWINDOW;
		HWND parentHandle = NULL;
		HMENU menuHandle = NULL;
		HINSTANCE instance = NULL;
		LPVOID param = NULL;

		handle = CreateWindow(className,name,style,0,0,0,0,parentHandle,menuHandle,instance,param );
		Window::windowMap[handle] = this;
	}

	LRESULT CALLBACK Window::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		RECT* rect;
		switch(message)
		{
		case WM_PAINT:		//wParam(unused) lParam(unused) return(0)
			if(view != 0)
			{
				view->PreDraw();
				view->Draw();
				view->PostDraw();
			}
			return 0;
		case WM_COMMAND:	//wParam(HI=type/LOW=id) lParam(HWND for Control) return(0)
			if(HIWORD(wParam)==0) //menu
				GetMenu()->OnWMCommand(LOWORD(wParam));
			else if(HIWORD(wParam)==1) //accelerator
				return 0;
			else //control
				return 0;
			return 0;
		case WM_SIZING:		//wParam(edge) lParam(RECT*) return(TRUE)
			if(delegatePtr == 0)
				return DefWindowProc(hWnd,message,wParam,lParam);
			else
			{
				rect = (RECT*)lParam;
				*rect = delegatePtr->WillResize(*rect);
				view->SetRect(rect->left,rect->top,rect->right,rect->bottom);
				return TRUE;
			}
		case WM_SIZE:		//wParam(type of resizing) lParam(new client area) return(0)
			return 0;
		case WM_CLOSE:		//wParam(unused) lParam(unused) return(0)
			if(delegatePtr == 0 || delegatePtr->ShouldClose())
			{
				Close();
				Application::DecWindowCount();
			}
			return 0;
		case WM_DESTROY:	//wParam(unused) lParam(unused) return(0)
			if(delegatePtr != 0)
				delegatePtr->WillClose();
			return 0;
		default:			//wParam() lParam() return()
			return DefWindowProc(hWnd,message,wParam,lParam);
		}
	}

	bool Window::Close()
	{
		windowMap.erase(handle);
		return DestroyWindow(handle);
	}

	LPWSTR Window::GetTitle()
	{
		WCHAR title[256];
		GetWindowText(handle,title,256);
		return title;
	}

	bool Window::SetTitle(LPWSTR title)
	{
		if(handle == 0)
			return 0;
		return SetWindowText(handle,title);
	}

	RECT Window::GetRect()
	{
		RECT r;
		GetWindowRect(handle,&r);
		return r;
	}
	
	bool Window::SetRect(int left, int top, int right, int bottom)
	{
		return MoveWindow(handle,left,top,right,bottom,true);
	}

	Menu* Window::GetMenu()
	{
		HMENU menu = ::GetMenu(handle);
		return Menu::menuMap[menu];
	}
	
	bool Window::SetMenu(Menu* menu)
	{
		return ::SetMenu(handle, menu->GetHandle());
	}

	void Window::Update()
	{
		UpdateWindow(handle);
	}

	View* Window::GetView()
	{
		return view;
	}

	bool Window::SetView(View* view)
	{
		view->SetHandle(this->GetHandle());
		this->view = view;
		return true;
	}

	void Window::Init()
	{
		DrawMenuBar(handle);
		ShowWindow(handle,SW_SHOW);
		Update();
		Application::IncWindowCount();
	}

	HWND Window::GetHandle()
	{
		return handle;
	}
	
	void Window::SetHandle(HWND handle)
	{
		this->handle = handle;
	}
}
